﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace ICommonLibrary.Collection {

    /// <summary>
    /// Thread safe queue with richer functionalities.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ReadWriteQueue<T> : ICollection {

        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
        private readonly Queue<T> _queue;
        private readonly object _syncRoot = new object();

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        public ReadWriteQueue() {
            _queue = new Queue<T>();
        }

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        /// <param name="capacity">the initial number of elements the queue can contain</param>
        public ReadWriteQueue(int capacity) {
            _queue = new Queue<T>(capacity);
        }

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        /// <param name="collection">the collection whose members are copied to the Queue</param>
        public ReadWriteQueue(IEnumerable<T> collection) {
            _queue = new Queue<T>(collection);
        }

        /// <summary>
        /// Returns an enumerator that enumerates through the collection
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator() {
            _lock.EnterReadLock();
            try {
                return new Queue<T>(_queue).GetEnumerator();
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Copies the Queue's elements to an existing array
        /// </summary>
        /// <param name="array">the one-dimensional array to copy into</param>
        /// <param name="index">the zero-based index at which copying begins</param>
        public void CopyTo(Array array, int index) {
            _lock.EnterReadLock();
            try {
                _queue.ToArray().CopyTo(array, index);
            }

            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Returns the number of items in the Queue
        /// </summary>
        public int Count {
            get {
                _lock.EnterReadLock();
                try {
                    return _queue.Count;
                }

                finally {
                    _lock.ExitReadLock();
                }
            }
        }

        public bool IsSynchronized {
            get { return true; }
        }

        public object SyncRoot {
            get { return _syncRoot; }
        }

        /// <summary>
        /// Returns an enumerator that enumerates through the collection
        /// </summary>
        public IEnumerator<T> GetEnumerator() {
            _lock.EnterReadLock();
            try {
                return ((IEnumerable<T>)new Queue<T>(_queue)).GetEnumerator();
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Copies the Queue's elements to an existing array
        /// </summary>
        /// <param name="array">the one-dimensional array to copy into</param>
        /// <param name="index">the zero-based index at which copying begins</param>
        public void CopyTo(T[] array, int index) {
            _lock.EnterReadLock();
            try {
                _queue.CopyTo(array, index);
            }

            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Adds an item to the queue
        /// </summary>
        /// <param name="item">the item to add to the queue</param>
        public void Enqueue(T item) {
            _lock.EnterWriteLock();
            try {
                _queue.Enqueue(item);
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Removes and returns the item in the beginning of the queue
        /// </summary>
        public T Dequeue() {
            _lock.EnterWriteLock();
            try {
                return _queue.Dequeue();
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Enqueues the list of items
        /// </summary>
        /// <param name="itemsToQueue">list of items to enqueue</param>
        public void EnqueueAll(IEnumerable<T> itemsToQueue) {
            _lock.EnterWriteLock();
            try {
                // loop through and add each item
                foreach (T item in itemsToQueue)
                    _queue.Enqueue(item);
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Enqueues the list of items
        /// </summary>
        /// <param name="itemsToQueue">list of items to enqueue</param>
        public void EnqueueAll(ReadWriteList<T> itemsToQueue) {
            _lock.EnterWriteLock();
            try {
                // loop through and add each item
                foreach (T item in itemsToQueue)
                    _queue.Enqueue(item);
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Dequeues all the items and returns them as a thread safe list
        /// </summary>
        public ReadWriteList<T> DequeueAll() {
            _lock.EnterWriteLock();
            try {
                // create return object
                var returnReadWriteList = new ReadWriteList<T>();

                // dequeue until everything is out
                while (_queue.Count > 0)
                    returnReadWriteList.Add(_queue.Dequeue());

                // return the list
                return returnReadWriteList;
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Removes all items from the queue
        /// </summary>
        public void Clear() {
            _lock.EnterWriteLock();
            try {
                _queue.Clear();
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Determines whether the item exists in the Queue
        /// </summary>
        /// <param name="item">the item to find in the queue</param>
        public bool Contains(T item) {
            _lock.EnterReadLock();
            try {
                return _queue.Contains(item);
            }

            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Returns the item at the start of the Queue without removing it
        /// </summary>
        public T Peek() {
            _lock.EnterReadLock();
            try {
                return _queue.Peek();
            }

            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Copies the Queue to a new array
        /// </summary>
        public T[] ToArray() {
            _lock.EnterReadLock();
            try {
                return _queue.ToArray();
            }

            finally {
                _lock.ExitReadLock();
            }
        }

        /// <summary>
        /// Sets the capacity of the Queue to the current number of items, if that number
        /// is less than 90 percent of the current capacity
        /// </summary>
        public void TrimExcess() {
            _lock.EnterWriteLock();
            try {
                _queue.TrimExcess();
            }

            finally {
                _lock.ExitWriteLock();
            }
        }

    }
}
